﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mimoza.Transport;

namespace Mimoza.AgentLayer
{
    /// <summary>
    /// Class for creating connection from host to server. 
    /// Also accept initiate connection signal from server.
    /// </summary>
    public class HostConnection : BaseObject
    {
        /// <summary>
        /// Delegate for processing connection that created by signal from server.
        /// </summary>
        public delegate void ConnectionCreated(Connection conn);


        public HostConnection(Configuration config)
            : base(config)
        {
            RegisterInTransport();
        }

        override public void Init()
        {
        }

        override public void Shutdown()
        {
            StopPeriodicalCreation();
            CloseCurrentConnection();
        }

        public ConnectionCreated ConnectionCreatedHandler
        {
            get { return m_creatingHandler; }
            set { m_creatingHandler = value; }
        }

        public void StartPeriodicalCreation(int period)
        {
            m_period = period * 1000;

            m_createConnectionThread = new System.Threading.Thread(new
                System.Threading.ThreadStart(PeriodicalCreateConnectionThread));

            m_actionEvent = new System.Threading.AutoResetEvent(false);

            m_createConnectionThread.Start();
        }

        public void StopPeriodicalCreation()
        {
            if (m_createConnectionThread == null) return;

            m_shutdownFlag = true;
            m_actionEvent.Set();

            bool currentThread = (m_createConnectionThread.ManagedThreadId == System.Threading.Thread.CurrentThread.ManagedThreadId);
            if (!currentThread) m_createConnectionThread.Join();

            m_createConnectionThread = null;
        }

        /// <summary>
        /// Create connection to server and call all handler (ConnectionCreatedHandler)
        /// </summary>
        public void DoServerConnect()
        {
            if (m_actionEvent!=null) m_actionEvent.Set();
        }

        /// <summary>
        /// Create connection to server
        /// </summary>
        public Transport.Connection CreateServerConnection()
        {
            if (Configuration.Settings.ServerAddr == "")
            {
                throw new AgentNotRegisterException();
            }

            Transport.Connection connection = CreateConnection();
            MakeHanshake(connection);

            return connection;
        }

        void RegisterInTransport()
        {
            Communication.RequestSignalHostConnection.RegisterHandler(
                new Communication.RequestSignalHostConnection.Handler(ProcessServerConnectionSignal));

            Communication.ReplyHostConnection.RegisterInTransport();
            Communication.RequestHostConnection.RegisterInTransport();
        }

        public Transport.Connection CreateConnection()
        {
            Transport.Connection connection = null;
            
            try
            {
                connection = Mimoza.Transport.Transport.CreateConnection(Configuration.Settings.ServerAddr,
                    Configuration.Settings.ServerTransportPort);
            }
            catch (Exception e)
            {
                Common.Logger.Log.Error("CreateServerConnection error occured err - " +
                    e.ToString() + ".");

                TryToResolveServerHost();

                throw new AgentCantConnectToServerException();
            }

            return connection;
        }

        void MakeHanshake(Transport.Connection connection)
        {
            Communication.RequestHostConnection request = new Communication.RequestHostConnection();
            
            request.HostId = Configuration.Settings.HostId;
            
            Communication.ReplyHostConnection reply = connection.MakeRequest(request) as
                Communication.ReplyHostConnection;

            if (reply == null || reply.HostId != Configuration.Settings.HostId)
            {
                Common.Logger.Log.Error("MakeHanshake wrong registration info serverHostId - " +
                    reply.HostId.ToString() + ".");

                throw new AgentCantConnectToServerException();
            }
        }

        void TryToResolveServerHost()
        {
            try
            {
                string serverAddr = Mimoza.Transport.EndPoint.GetHostAddrByName(Configuration.Settings.ServerHost);

                if (!string.IsNullOrEmpty(serverAddr) && serverAddr != Configuration.Settings.ServerAddr)
                {
                    Common.Logger.Log.Info("ResolveServerHost server address is changed new - {0} old - {1} name - {2}.",
                        serverAddr, Configuration.Settings.ServerAddr, Configuration.Settings.ServerHost);

                    Configuration.Settings.ServerAddr = serverAddr;

                    Settings.Save(Configuration.Settings);
                }
            }
            catch (Exception e)
            {
                Common.Logger.Log.Error("ResolveServerHost can't resolve server name - {0} err - {1}.",
                    Configuration.Settings.ServerHost, e.ToString());
            }
        }

        TransferObject ProcessServerConnectionSignal(Communication.RequestSignalHostConnection request)
        {
            DoServerConnect();
            return null;
        }

        void PeriodicalCreateConnectionThread()
        {
            Common.Logger.Log.Info("HostConnection.PeriodicalCreateConnectionThread (period - " +
                m_period.ToString() + ")...");

            while (true)
            {
                PeriodicalCreateConnection();
                bool bWaitRes = m_actionEvent.WaitOne(m_period, false);
                if (bWaitRes == true && m_shutdownFlag) break;
            }

            Common.Logger.Log.Info("HostConnection.PeriodicalCreateConnectionThread...OK");
        }

        void PeriodicalCreateConnection()
        {
            Connection conn = null;
            try
            {
                conn = CreateServerConnection();
            
                if (ConnectionCreatedHandler != null && conn != null)
                {
                    ConnectionCreatedHandler(conn);
                }
            }
            catch (Exception e)
            {
                string outStr = "PeriodicalCreateConnection error occured err - " + e.ToString() + ".";
                Common.Logger.Log.Error(outStr);

                //System.Diagnostics.Debug.Assert(false, outStr);
            }

            if (conn != null)
            {
                conn.Close();
                conn = null;
            }
        }

        void CloseCurrentConnection()
        {
            if (m_serverInitiateConn != null)
            {
                m_serverInitiateConn.Close();
            }
        }

        private Connection m_serverInitiateConn = null;
        ConnectionCreated m_creatingHandler;
        int m_period = 10000; // default 10 sec

        System.Threading.Thread m_createConnectionThread;
        System.Threading.AutoResetEvent m_actionEvent;
        bool m_shutdownFlag = false;
    }
}

